/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Description: Hmfs-tools resize node
 */

#include "node.h"
#include "securec.h"

namespace OHOS {
namespace Hmfs {
unsigned long CurrentNatAddr(std::shared_ptr<HmfsSuperBlockInfo> &superBlockInfo, uint32_t start)
{
    if (superBlockInfo == nullptr) {
        return 0;
    }
    NodeAddressTable *nat = superBlockInfo->nodeAddrTable;
    unsigned long blockOffset = NatBlockOffset(start);
    int32_t segmentOffset = blockOffset >> superBlockInfo->logBlksPerSeg;
    unsigned long blockAddr = static_cast<unsigned long>(nat->natBlockddr +
        (segmentOffset << superBlockInfo->logBlksPerSeg << 1) +
        (blockOffset & ((1 << superBlockInfo->logBlksPerSeg) - 1)));
    if (HmfsCommon::GetInstance().VerifyBit(blockOffset, nat->natBitmap)) {
        blockAddr += superBlockInfo->blocksPerSeg;
    }
    return blockAddr;
}

bool IsRecoverableDnode(std::shared_ptr<HmfsSuperBlockInfo> &superBlockInfo, NodeData *nodeBlock)
{
    if (nodeBlock == nullptr || superBlockInfo == nullptr) {
        HMFS_ERROR("nodeBlock or superBlockInfo is nullptr");
        return false;
    }
    CheckPointData *checkPointData = superBlockInfo->checkPointData;
    uint64_t checkpointVersion = checkPointData->checkPointVersion;
    constexpr uint32_t bitShift = 32;
    if (IsSetCheckPointFlags(checkPointData, CP_FLAG_NOCRC_RECOVERY)) {
        return (checkpointVersion << bitShift) == (nodeBlock->footer.checkPointVersion << bitShift);
    }
    if (IsSetCheckPointFlags(checkPointData, CP_FLAG_CRC_RECOVERY)) {
        checkpointVersion |= (GetCheckpointCrc(checkPointData) << bitShift);
    }
    return checkpointVersion == nodeBlock->footer.checkPointVersion;
}

int32_t LookupNatInJournal(std::shared_ptr<HmfsSuperBlockInfo> &superBlockInfo, uint32_t nodeId, NatEntry *rawNat)
{
    if (superBlockInfo == nullptr || rawNat == nullptr) {
        return EXIT_ERR_CODE;
    }
    CurrentSegmentInfo *curSeg = GetCurrentSegment(superBlockInfo, CURSEG_DATA_HOT);
    JournalEntry *journal = &curSeg->summaryBlockData->journal;
    for (int32_t i = 0; i < NATS_IN_CURSUM(journal); i++) {
        if (LE32ToNative(NID_IN_JOURNAL(journal, i)) == nodeId) {
            if (memcpy_s(rawNat, sizeof(NatEntry), &NAT_IN_JOURNAL(journal, i), sizeof(NatEntry)) != 0) {
                return EXIT_ERR_CODE;
            }
            HMFS_INFO("==> Found nodeId [0x%x] in nat cache\n", nodeId);
            return i;
        }
    }
    return EXIT_ERR_CODE;
}

void GetNatEntry(std::shared_ptr<HmfsSuperBlockInfo> &superBlockInfo, uint32_t nodeId, NatEntry *rawNat)
{
    if (superBlockInfo == nullptr || rawNat == nullptr || LookupNatInJournal(superBlockInfo, nodeId, rawNat) >= 0) {
        return;
    }
    NatBlockData *natBlock = static_cast<NatBlockData *>(calloc(BLOCK_SZ, 1));
    HMFS_ASSERT(natBlock);
    uint32_t entryOff = nodeId % NAT_ENTRY_PER_BLOCK;
    unsigned long blockAddr = CurrentNatAddr(superBlockInfo, nodeId);
    int32_t ret = HmfsIo::GetInstance().DevReadBlock(natBlock, blockAddr);
    HMFS_ASSERT(ret >= 0);
    if (memcpy_s(rawNat, sizeof(NatEntry), &natBlock->entries[entryOff], sizeof(NatEntry)) != 0) {
        HMFS_ERROR("memcpy failed");
    }
    free(natBlock);
}

uint32_t GetAddressesPerPage(std::shared_ptr<HmfsSuperBlockInfo> &superBlockInfo,
    NodeData *nodeBlock, NodeData *inodeBlock)
{
    if (nodeBlock == nullptr || superBlockInfo == nullptr || inodeBlock == nullptr) {
        HMFS_ERROR("nodeBlock or superBlockInfo or inodeBlock is nullptr");
        return 1;
    }
    uint32_t inodeNumber = LE32ToNative(nodeBlock->footer.inodeNumber);
    if (IsInode(nodeBlock)) {
        return HmfsCommon::GetInstance().AddrsPerInode(&nodeBlock->inode);
    }
    uint32_t nodeBlocks = 0;
    if (inodeBlock == nullptr) {
        constexpr uint32_t inodeBlockMultiplier = 2;
        inodeBlock = static_cast<NodeData *>(calloc(BLOCK_SZ, inodeBlockMultiplier));
        HMFS_ASSERT(inodeBlock);
        NodeInfo nodeId = {};
        GetNodeInfo(superBlockInfo, inodeNumber, &nodeId);
        HMFS_ASSERT(HmfsIo::GetInstance().DevReadBlock(inodeBlock, nodeId.blockAddr) >= 0);
        nodeBlocks = HmfsCommon::GetInstance().AddrsPerBlock(&inodeBlock->inode);
        free(inodeBlock);
    } else {
        nodeBlocks = HmfsCommon::GetInstance().AddrsPerBlock(&inodeBlock->inode);
    }
    return nodeBlocks;
}

uint32_t CalculateBlockIndex(uint32_t nodeOffset, NodeData *nodeBlock)
{
    if (nodeBlock == nullptr) {
        return 0;
    }
    constexpr uint32_t directBlockLimit = 2;
    constexpr uint32_t indirectBlockStart = 4;
    constexpr uint32_t doubleIndirectBlockStart = 5;
    constexpr uint32_t doubleIndirectOffsetAdjustment = 3;
    uint32_t indirectBlks = directBlockLimit * NIDS_PER_BLOCK + indirectBlockStart;
    uint32_t blockIndex = 0;
    if (nodeOffset <= directBlockLimit) {
        blockIndex = nodeOffset - 1;
    } else if (nodeOffset <= indirectBlks) {
        uint32_t dec = (nodeOffset - indirectBlockStart) / (NIDS_PER_BLOCK + 1);
        blockIndex = nodeOffset - directBlockLimit - dec;
    } else {
        uint32_t dec = (nodeOffset - indirectBlks - doubleIndirectOffsetAdjustment) / (NIDS_PER_BLOCK + 1);
        blockIndex = nodeOffset - doubleIndirectBlockStart - dec;
    }
    return blockIndex * HmfsCommon::GetInstance().AddrsPerBlock(&nodeBlock->inode) +
        HmfsCommon::GetInstance().AddrsPerInode(&nodeBlock->inode);
}
} // namespace Hmfs
} // namespace OHOS